#include <QtGui>
#include <QDebug>
#include <cmath>
#include "ventana.h"

Ventana::Ventana( QWidget *parent ) : QWidget( parent ) {
	solucionado = false;
	mCampo.l = 400;
	mCampo.w = 50;
	resize( 800, 600 );
}

void Ventana::dibujarCirculo( QPainter &p, const Circulo &c, const QColor &color ) {
	int mitad = mCampo.y + mCampo.w / 2;
	int inicio = mCampo.x + c.dist;
	
	p.setPen( color );
	p.drawEllipse( QPointF( inicio, mitad ), c.radio, c.radio );
	
	if ( c.radioIntervalo >= 0 ) {
		int a = inicio - c.radioIntervalo;
		int b = inicio + c.radioIntervalo;
		p.drawLine( a, mitad,b, mitad );
		p.drawLine( a, mCampo.y, a, mCampo.y + mCampo.w );
		p.drawLine( b, mCampo.y, b, mCampo.y + mCampo.w );
	}
	
	p.setPen( Qt::red );
	p.drawPoint( mCampo.x + c.dist, mitad );
}

void Ventana::paintEvent( QPaintEvent *event ) {
	QPainter p ( this );
	
	// Dibujamos el campo.
	p.drawRect( mCampo.x, mCampo.y, mCampo.l, mCampo.w );
	
	// Dibujamos la solución.
	if ( solucionado ) {
		foreach ( const Circulo &c, mCirculos ) {
			dibujarCirculo( p, c, c.solucion ? Qt::yellow : Qt::black );
		}
		return;
	}
	
	// Dibujamos todos los círculos.
	float a0 = mActual.a();
	float b0 = mActual.b();
	bool intersecta = false;
	foreach ( const Circulo &c, mCirculos ) {
		float a1 = c.a();
		float b1 = c.b();
		
		if ( (b0 >= a1 && b0 <= b1) || (a0 >= a1 && a0 <= b1) || (a0 >= a1 && b0 <= b1) || (a0 <= a1 && b0 >= b1) ) {
			dibujarCirculo( p, c, Qt::yellow );
			intersecta = true;
		} else {
			dibujarCirculo( p, c );
		}
	}
	
	// Dibujamos el círculo actual.
	dibujarCirculo( p, mActual, intersecta ? Qt::yellow : Qt::black );
}

void Ventana::calcularTamano( int ancho, int alto ) {
	mCampo.x = (ancho - mCampo.l) / 2;
	mCampo.y = (alto - mCampo.w) / 2;
	
	mActual.radioIntervalo = std::sqrt( mActual.radio * mActual.radio - mCampo.w * mCampo.w / 4.0f );
}

void Ventana::resizeEvent( QResizeEvent *event ) {
	const QSize &s = event->size();
	calcularTamano( s.width(), s.height() );
}

int Ventana::resolver() {
	int len = mCirculos.size();
	int aspersores = 1;
	const QVector<Circulo> &intervalos = mCirculos;
	int l = mCampo.l;

	// Ordenamos y le quitamos la solución anterior.
	qSort( mCirculos );

#ifndef NDEBUG
	qDebug() << '[';
	foreach( const Circulo &c, mCirculos ) {
		qDebug() << '(' << c.a() << ',' << c.b() << ')';
	}
	qDebug() << ']';
#endif
	
	// Elegimos el primero.
	// que llegue más lejos.
	int primero = -1;
	for ( int i = 0; i < len && intervalos[i].a() <= 0.0f; i++ ) {
		if ( primero == -1 )
			primero = i;
		else if ( intervalos[i].b() > intervalos[primero].b() )
			primero = i;
	}

	if ( primero == -1 ) return -1;
	intervalos[primero].solucion = true;

	// Ahora elegimos el resto, ya tenemos el primero que
	// llega más lejos así que partimos de ahí :-)
	int intersecta = -1;
	for ( int i = primero; i < len; i = intersecta ) {
		if ( intervalos[i].b() >= l )
			return aspersores;

		intersecta = -1;
		for ( int j = i + 1; j < len && intervalos[j].a() <= intervalos[i].b(); j++ ) {
			if ( intersecta == -1 )
				intersecta = j;
			else if ( intervalos[j].b() > intervalos[intersecta].b() )
				intersecta = j;
		}

		if ( intersecta == -1 )
			return -1;

		intervalos[intersecta].solucion = true;
		aspersores++;
	}

	if ( intersecta == -1 && intervalos[primero].b() >= l )
		return aspersores;
	else if ( intersecta >= 0 && intervalos[intersecta].b() >= l )
		return aspersores;
	
	return -1;
}

void Ventana::keyPressEvent( QKeyEvent *event ) {
	static const float diferencia = 2.0f;
	
	switch ( event->key() ) {
		case Qt::Key_Left:
			mActual.dist -= diferencia;
			break;
		case Qt::Key_Right:
			mActual.dist += diferencia;
			break;
		case Qt::Key_Up:
			mActual.radio += diferencia;
			break;
		case Qt::Key_Down:
			mActual.radio -= diferencia;
			break;
		case Qt::Key_Home:
			mCampo.l -= diferencia;
			break;
		case Qt::Key_End:
			mCampo.l += diferencia;
			break;
		case Qt::Key_PageUp:
			mCampo.w += diferencia;
			break;
		case Qt::Key_PageDown:
			mCampo.w -= diferencia;
			break;
		case Qt::Key_Enter:
		case Qt::Key_Return:
			mCirculos.append( mActual );
			break;
		case Qt::Key_Escape:
			qApp->quit();
			break;
		case Qt::Key_R:
			if ( solucionado ) {
				solucionado = false;
			} else {
				int res = resolver();
				if ( res == -1 ) {
					QMessageBox::information( this, "Resultado", "No tiene solución." );
				} else {
					QMessageBox::information( this, "Resultado", QString( "Se cubre todo el campo con %1 aspersores." ).arg( res ) );
				}
				solucionado = true;
			}
			break;
		case Qt::Key_C:
			solucionado = false;
			mCirculos.clear();
			break;
		case Qt::Key_O: {
			static const int escala = 10;
			QString archivo = QFileDialog::getOpenFileName( this, "Abrir", QString(), "*.*" );
			QFile f ( archivo );
			if ( !f.open( QIODevice::ReadOnly | QIODevice::Text ) ) {
				QMessageBox::critical( this, "Error", "No se pudo abrir el archivo." );
				return;
			}
			QTextStream entrada( &f );
			
			mCirculos.clear();
			
			int n, l, w;
			entrada >> n >> l >> w;
			
			qDebug() << n << l << w;
			
			mCampo.l = l * escala;
			mCampo.w = w * escala;
			
			for ( int i = 0; i < n; i++ ) {
				int d, r;
				entrada >> d >> r;
				Circulo c( d * escala, r * escala );
				c.radioIntervalo = std::sqrt( c.radio * c.radio - mCampo.w * mCampo.w / 4.0f );
				mCirculos.append( c );
			}
			break;
		}
		default:
			return;
	}
	
	calcularTamano( width(), height() );
	update( 0, 0, width(), height() );
}